﻿using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Text.RegularExpressions;
using System.Windows.Media;

namespace SLCanvas
{
    static class Utility
    {
        // Attempt to parse a string style; return results if successful
        public static bool ParseStyle(string style, out Brush brush, out string effectiveStyle)
        {
            brush = null;
            effectiveStyle = null;
            Color color;
            if (ParseCssColor(style, out color))
            {
                // Create a brush and style string for it
                brush = new SolidColorBrush(color);
                if (byte.MaxValue == color.A)
                {
                    // Use 6-character form
                    effectiveStyle = string.Format(CultureInfo.InvariantCulture, "#{0:x2}{1:x2}{2:x2}", color.R, color.G, color.B);
                }
                else
                {
                    // Use 8-character form
                    effectiveStyle = string.Format(CultureInfo.InvariantCulture, "rgba({0}, {1}, {2}, {3})", color.R, color.G, color.B, color.A / ((double)byte.MaxValue));
                }
            }
            else
            {
                // Unable to parse
                return false;
            }
            return true;
        }

        // Clones a PathGeometry because Silverlight doesn't support reuse
        public static PathGeometry ClonePathGeometry(PathGeometry pathGeometry)
        {
            var pathGeometryClone = new PathGeometry();
            foreach (var figure in pathGeometry.Figures)
            {
                var figureClone = new PathFigure { StartPoint = figure.StartPoint, IsClosed = figure.IsClosed, IsFilled = figure.IsFilled };
                foreach (var segment in figure.Segments)
                {
                    var lineSegment = segment as LineSegment;
                    var arcSegment = segment as ArcSegment;
                    var bezierSegment = segment as BezierSegment;
                    var quadraticBezierSegment = segment as QuadraticBezierSegment;
                    if (null != lineSegment)
                    {
                        figureClone.Segments.Add(new LineSegment { Point = lineSegment.Point });
                    }
                    else if (null != arcSegment)
                    {
                        figureClone.Segments.Add(new ArcSegment { Point = arcSegment.Point, RotationAngle = arcSegment.RotationAngle, Size = arcSegment.Size, SweepDirection = arcSegment.SweepDirection, IsLargeArc = arcSegment.IsLargeArc });
                    }
                    else if (null != quadraticBezierSegment)
                    {
                        figureClone.Segments.Add(new QuadraticBezierSegment { Point1 = quadraticBezierSegment.Point1, Point2 = quadraticBezierSegment.Point2 });
                    }
                    else if (null != bezierSegment)
                    {
                        figureClone.Segments.Add(new BezierSegment { Point1 = bezierSegment.Point1, Point2 = bezierSegment.Point2, Point3 = bezierSegment.Point3 });
                    }
                    else
                    {
                        throw new NotSupportedException();
                    }
                }
                pathGeometryClone.Figures.Add(figureClone);
            }
            return pathGeometryClone;
        }

        // Clones a TransformGroup for use with a new Canvas
        public static TransformGroup CloneTransformGroup(TransformGroup transformGroup)
        {
            var transformGroupClone = new TransformGroup();
            foreach (var transform in transformGroup.Children)
            {
                var translateTransform = transform as TranslateTransform;
                var rotateTransform = transform as RotateTransform;
                var scaleTransform = transform as ScaleTransform;
                if (null != translateTransform)
                {
                    transformGroupClone.Children.Add(new TranslateTransform { X = translateTransform.X, Y = translateTransform.Y });
                }
                else if (null != rotateTransform)
                {
                    transformGroupClone.Children.Add(new RotateTransform { Angle = rotateTransform.Angle });
                }
                else if (null != scaleTransform)
                {
                    transformGroupClone.Children.Add(new ScaleTransform { ScaleX = scaleTransform.ScaleX, ScaleY = scaleTransform.ScaleY });
                }
                else
                {
                    throw new NotSupportedException();
                }
            }
            return transformGroupClone;
        }

        // Clones a GradientBrush so a TranslateTransform can be applied
        public static Brush CloneAndMapGradientBrush(Brush brush, double x, double y)
        {
            var gradientBrush = brush as GradientBrush;
            if (null == gradientBrush)
            {
                return brush;
            }
            GradientBrush gradientBrushClone = null;
            var linearGradientBrush = gradientBrush as LinearGradientBrush;
            var radialGradientBrush = gradientBrush as RadialGradientBrush;
            if (null != linearGradientBrush)
            {
                var linearGradientBrushClone = new LinearGradientBrush();
                linearGradientBrushClone.StartPoint = linearGradientBrush.StartPoint;
                linearGradientBrushClone.EndPoint = linearGradientBrush.EndPoint;
                gradientBrushClone = linearGradientBrushClone;
            }
            else if (null != radialGradientBrush)
            {
                var radialGradientBrushClone = new RadialGradientBrush();
                radialGradientBrushClone.GradientOrigin = radialGradientBrush.GradientOrigin;
                radialGradientBrushClone.Center = radialGradientBrush.Center;
                radialGradientBrushClone.RadiusX = radialGradientBrush.RadiusX;
                radialGradientBrushClone.RadiusY = radialGradientBrush.RadiusY;
                gradientBrushClone = radialGradientBrushClone;
            }
            else
            {
                throw new NotSupportedException();
            }
            foreach (var gradientStop in gradientBrush.GradientStops)
            {
                gradientBrushClone.GradientStops.Add(new GradientStop { Color = gradientStop.Color, Offset = gradientStop.Offset });
            }
            gradientBrushClone.MappingMode = gradientBrush.MappingMode;
            gradientBrushClone.Transform = new TranslateTransform { X = -x, Y = -y };
            return gradientBrushClone;
        }

        private static readonly Regex _hex6Regex = new Regex(@"#[0-9a-fA-F]{6}");
        private static readonly Regex _hex3Regex = new Regex(@"#[0-9a-fA-F]{3}");
        private static readonly Regex _rgbRegex = new Regex(@"rgb\(\s*(?<r>\d+)\s*,\s*(?<g>\d+)\s*,\s*(?<b>\d+)\s*\)");
        private static readonly Regex _rgbaRegex = new Regex(@"rgba\(\s*(?<r>\d+)\s*,\s*(?<g>\d+)\s*,\s*(?<b>\d+)\s*,\s*(?<a>.+)\s*\)");

        // Attempt to parse a color by the various allowed forms
        public static bool ParseCssColor(string cssColor, out Color color)
        {
            color = Colors.Transparent;
            cssColor = CssColorToHex6(cssColor);
            var hex6Match = _hex6Regex.Match(cssColor);
            var hex3Match = _hex3Regex.Match(cssColor);
            var rgbMatch = _rgbRegex.Match(cssColor);
            var rgbaMatch = _rgbaRegex.Match(cssColor);
            if (hex6Match.Success)
            {
                color = Color.FromArgb(byte.MaxValue, byte.Parse(cssColor.Substring(1, 2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture), byte.Parse(cssColor.Substring(3, 2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture), byte.Parse(cssColor.Substring(5, 2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture));
            }
            else if (hex3Match.Success)
            {
                color = Color.FromArgb(byte.MaxValue, (byte)(byte.Parse(cssColor.Substring(1, 1), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture) << 4), (byte)(byte.Parse(cssColor.Substring(2, 1), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture) << 4), (byte)(byte.Parse(cssColor.Substring(3, 1), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture) << 4));
            }
            else if (rgbMatch.Success)
            {
                color = Color.FromArgb(byte.MaxValue, byte.Parse(rgbMatch.Groups["r"].Value, CultureInfo.InvariantCulture), byte.Parse(rgbMatch.Groups["g"].Value, CultureInfo.InvariantCulture), byte.Parse(rgbMatch.Groups["b"].Value, CultureInfo.InvariantCulture));
            }
            else if (rgbaMatch.Success)
            {
                color = Color.FromArgb((byte)(byte.MaxValue * double.Parse(rgbaMatch.Groups["a"].Value, CultureInfo.InvariantCulture)), byte.Parse(rgbaMatch.Groups["r"].Value, CultureInfo.InvariantCulture), byte.Parse(rgbaMatch.Groups["g"].Value, CultureInfo.InvariantCulture), byte.Parse(rgbaMatch.Groups["b"].Value, CultureInfo.InvariantCulture));
            }
            else
            {
                return false;
            }
            return true;
        }

        // Looks up the string color in the list of CSS color mappings
        [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "CSS spec gives color names in lower case.")]
        private static string CssColorToHex6(string color)
        {
            color = color.ToLower(CultureInfo.InvariantCulture);
            return _cssColorToHex6.ContainsKey(color) ? _cssColorToHex6[color] : color;
        }

        // From http://www.w3.org/TR/css3-color/
        private static Dictionary<string, string> _cssColorToHex6 = new Dictionary<string, string>
        {
            {"aliceblue", "#f0f8ff"},
            {"antiquewhite", "#faebd7"},
            {"aqua", "#00ffff"},
            {"aquamarine", "#7fffd4"},
            {"azure", "#f0ffff"},
            {"beige", "#f5f5dc"},
            {"bisque", "#ffe4c4"},
            {"black", "#000000"},
            {"blanchedalmond", "#ffebcd"},
            {"blue", "#0000ff"},
            {"blueviolet", "#8a2be2"},
            {"brown", "#a52a2a"},
            {"burlywood", "#deb887"},
            {"cadetblue", "#5f9ea0"},
            {"chartreuse", "#7fff00"},
            {"chocolate", "#d2691e"},
            {"coral", "#ff7f50"},
            {"cornflowerblue", "#6495ed"},
            {"cornsilk", "#fff8dc"},
            {"crimson", "#dc143c"},
            {"cyan", "#00ffff"},
            {"darkblue", "#00008b"},
            {"darkcyan", "#008b8b"},
            {"darkgoldenrod", "#b8860b"},
            {"darkgray", "#a9a9a9"},
            {"darkgreen", "#006400"},
            {"darkgrey", "#a9a9a9"},
            {"darkkhaki", "#bdb76b"},
            {"darkmagenta", "#8b008b"},
            {"darkolivegreen", "#556b2f"},
            {"darkorange", "#ff8c00"},
            {"darkorchid", "#9932cc"},
            {"darkred", "#8b0000"},
            {"darksalmon", "#e9967a"},
            {"darkseagreen", "#8fbc8f"},
            {"darkslateblue", "#483d8b"},
            {"darkslategray", "#2f4f4f"},
            {"darkslategrey", "#2f4f4f"},
            {"darkturquoise", "#00ced1"},
            {"darkviolet", "#9400d3"},
            {"deeppink", "#ff1493"},
            {"deepskyblue", "#00bfff"},
            {"dimgray", "#696969"},
            {"dimgrey", "#696969"},
            {"dodgerblue", "#1e90ff"},
            {"firebrick", "#b22222"},
            {"floralwhite", "#fffaf0"},
            {"forestgreen", "#228b22"},
            {"fuchsia", "#ff00ff"},
            {"gainsboro", "#dcdcdc"},
            {"ghostwhite", "#f8f8ff"},
            {"gold", "#ffd700"},
            {"goldenrod", "#daa520"},
            {"gray", "#808080"},
            {"green", "#008000"},
            {"greenyellow", "#adff2f"},
            {"grey", "#808080"},
            {"honeydew", "#f0fff0"},
            {"hotpink", "#ff69b4"},
            {"indianred", "#cd5c5c"},
            {"indigo", "#4b0082"},
            {"ivory", "#fffff0"},
            {"khaki", "#f0e68c"},
            {"lavender", "#e6e6fa"},
            {"lavenderblush", "#fff0f5"},
            {"lawngreen", "#7cfc00"},
            {"lemonchiffon", "#fffacd"},
            {"lightblue", "#add8e6"},
            {"lightcoral", "#f08080"},
            {"lightcyan", "#e0ffff"},
            {"lightgoldenrodyellow", "#fafad2"},
            {"lightgray", "#d3d3d3"},
            {"lightgreen", "#90ee90"},
            {"lightgrey", "#d3d3d3"},
            {"lightpink", "#ffb6c1"},
            {"lightsalmon", "#ffa07a"},
            {"lightseagreen", "#20b2aa"},
            {"lightskyblue", "#87cefa"},
            {"lightslategray", "#778899"},
            {"lightslategrey", "#778899"},
            {"lightsteelblue", "#b0c4de"},
            {"lightyellow", "#ffffe0"},
            {"lime", "#00ff00"},
            {"limegreen", "#32cd32"},
            {"linen", "#faf0e6"},
            {"magenta", "#ff00ff"},
            {"maroon", "#800000"},
            {"mediumaquamarine", "#66cdaa"},
            {"mediumblue", "#0000cd"},
            {"mediumorchid", "#ba55d3"},
            {"mediumpurple", "#9370db"},
            {"mediumseagreen", "#3cb371"},
            {"mediumslateblue", "#7b68ee"},
            {"mediumspringgreen", "#00fa9a"},
            {"mediumturquoise", "#48d1cc"},
            {"mediumvioletred", "#c71585"},
            {"midnightblue", "#191970"},
            {"mintcream", "#f5fffa"},
            {"mistyrose", "#ffe4e1"},
            {"moccasin", "#ffe4b5"},
            {"navajowhite", "#ffdead"},
            {"navy", "#000080"},
            {"oldlace", "#fdf5e6"},
            {"olive", "#808000"},
            {"olivedrab", "#6b8e23"},
            {"orange", "#ffa500"},
            {"orangered", "#ff4500"},
            {"orchid", "#da70d6"},
            {"palegoldenrod", "#eee8aa"},
            {"palegreen", "#98fb98"},
            {"paleturquoise", "#afeeee"},
            {"palevioletred", "#db7093"},
            {"papayawhip", "#ffefd5"},
            {"peachpuff", "#ffdab9"},
            {"peru", "#cd853f"},
            {"pink", "#ffc0cb"},
            {"plum", "#dda0dd"},
            {"powderblue", "#b0e0e6"},
            {"purple", "#800080"},
            {"red", "#ff0000"},
            {"rosybrown", "#bc8f8f"},
            {"royalblue", "#4169e1"},
            {"saddlebrown", "#8b4513"},
            {"salmon", "#fa8072"},
            {"sandybrown", "#f4a460"},
            {"seagreen", "#2e8b57"},
            {"seashell", "#fff5ee"},
            {"sienna", "#a0522d"},
            {"silver", "#c0c0c0"},
            {"skyblue", "#87ceeb"},
            {"slateblue", "#6a5acd"},
            {"slategray", "#708090"},
            {"slategrey", "#708090"},
            {"snow", "#fffafa"},
            {"springgreen", "#00ff7f"},
            {"steelblue", "#4682b4"},
            {"tan", "#d2b48c"},
            {"teal", "#008080"},
            {"thistle", "#d8bfd8"},
            {"tomato", "#ff6347"},
            {"turquoise", "#40e0d0"},
            {"violet", "#ee82ee"},
            {"wheat", "#f5deb3"},
            {"white", "#ffffff"},
            {"whitesmoke", "#f5f5f5"},
            {"yellow", "#ffff00"},
            {"yellowgreen", "#9acd32"},
        };
    }
}
